//
// GPSMapEdit
// (c) Konstantin Galichsky (kg@geopainting.com), 2002-2005
//
// Fast rounding of floating-point numbers.
//

# include "StdAfx.h"
# include "fast_floor.h"

	// CRT's ::floor() and float-to-long cast appear too slow (takes hundreds of CPU ticks).
	// NOTE: IEEE format is assumed.
long fast_floor (float _f) {
	DWORD dw = reinterpret_cast<DWORD &> (_f);
	if (long (dw) < 0) {
		//
		// For negative values.
		//
		dw &= 0x7FFFFFFF;
		if (dw == 0)
			return 0;

		const int sh = 23 + 127 - (dw >> 23);
		if (sh >= 24)
			return -1;
		else if (sh < 0) {
			// NOTE: precision is lost.
			return - long ((0x00800000 | (dw & 0x007FFFFF)) << (-sh));
		} else {
			if (dw & (0x007FFFFF >> (23 - sh)))
				// NOTE: the number has fractional part.
				return - long ((0x00800000 | (dw & 0x007FFFFF)) >> sh) - 1;
			else
				// NOTE: the number is whole.
				return - long ((0x00800000 | (dw & 0x007FFFFF)) >> sh);
		}
	} else {
		//
		// For positive values.
		//
		if (dw == 0)
			return 0;

		const int sh = 23 + 127 - (dw >> 23);
		if (sh >= 24)
			return 0;
		else if (sh < 0)
			// NOTE: the precision is lost.
			return (0x00800000 | (dw & 0x007FFFFF)) << (-sh);
		else 
			return (0x00800000 | (dw & 0x007FFFFF)) >> sh;
	}
}

long fast_floor_d (double _f) {
	ULONGLONG qw = reinterpret_cast<ULONGLONG &> (_f);
	if (qw & 0x8000000000000000) {
		//
		// For negative values.
		//
		qw &= 0x7FFFFFFFFFFFFFFF;
		if (qw == 0)
			return 0;

		const int sh = 52 + 1023 - (qw >> 52);
		if (sh >= 53)
			return -1;
		else if (sh < 0) {
			// NOTE: precision is lost.
			return - long ((0x0010000000000000 | (qw & 0x000FFFFFFFFFFFFF)) << (-sh));
		} else {
			if (qw & (0x000FFFFFFFFFFFFF >> (52 - sh)))
				// NOTE: the number has fractional part.
				return - long ((0x0010000000000000 | (qw & 0x000FFFFFFFFFFFFF)) >> sh) - 1;
			else
				// NOTE: the number is whole.
				return - long ((0x0010000000000000 | (qw & 0x000FFFFFFFFFFFFF)) >> sh);
		}
	} else {
		//
		// For positive values.
		//
		if (qw == 0)
			return 0;

		const int sh = 52 + 1023 - (qw >> 52);
		if (sh >= 53)
			return 0;
		else if (sh < 0)
			// NOTE: the precision is lost.
			return (0x0010000000000000 | (qw & 0x000FFFFFFFFFFFFF)) << (-sh);
		else 
			return (0x0010000000000000 | (qw & 0x000FFFFFFFFFFFFF)) >> sh;
	}
}

# ifdef _DEBUG

void Test_fast_floor () {
	assert (fast_floor (0.f) == 0);
	assert (fast_floor (0.000001f) == 0);
	assert (fast_floor (0.1f) == 0);
	assert (fast_floor (0.4f) == 0);
	assert (fast_floor (0.5f) == 0);
	assert (fast_floor (0.6f) == 0);
	assert (fast_floor (0.75f) == 0);
	assert (fast_floor (0.76f) == 0);
	assert (fast_floor (0.9f) == 0);
	assert (fast_floor (1.f) == 1);
	assert (fast_floor (1.1f) == 1);
	assert (fast_floor (1.9f) == 1);
	assert (fast_floor (3.f) == 3);
	assert (fast_floor (3.5f) == 3);
	assert (fast_floor (4.f) == 4);
	assert (fast_floor (-1.f) == -1);
	assert (fast_floor (-0.000001f) == -1);
	assert (fast_floor (-.4f) == -1);
	assert (fast_floor (-.5f) == -1);
	assert (fast_floor (-.6f) == -1);
	assert (fast_floor (-.75f) == -1);
	assert (fast_floor (-.76f) == -1);
	assert (fast_floor (-.1f) == -1);
	assert (fast_floor (-1.f) == -1);
	assert (fast_floor (-1.1f) == -2);
	assert (fast_floor (-1.9f) == -2);
	assert (fast_floor (-3.f) == -3);
	assert (fast_floor (-3.5f) == -4);
	assert (fast_floor (-4.f) == -4);
//	assert (fast_floor (1000000000) == 1000000000);

	assert (fast_floor_d (0.) == 0);
	assert (fast_floor_d (0.000001) == 0);
	assert (fast_floor_d (0.1) == 0);
	assert (fast_floor_d (0.4) == 0);
	assert (fast_floor_d (0.5) == 0);
	assert (fast_floor_d (0.6) == 0);
	assert (fast_floor_d (0.75) == 0);
	assert (fast_floor_d (0.76) == 0);
	assert (fast_floor_d (0.9) == 0);
	assert (fast_floor_d (1.) == 1);
	assert (fast_floor_d (1.1) == 1);
	assert (fast_floor_d (1.9) == 1);
	assert (fast_floor_d (3.) == 3);
	assert (fast_floor_d (3.5) == 3);
	assert (fast_floor_d (4.) == 4);
	assert (fast_floor_d (-1.) == -1);
	assert (fast_floor_d (-0.000001) == -1);
	assert (fast_floor_d (-.4) == -1);
	assert (fast_floor_d (-.5) == -1);
	assert (fast_floor_d (-.6) == -1);
	assert (fast_floor_d (-.75) == -1);
	assert (fast_floor_d (-.76) == -1);
	assert (fast_floor_d (-.1) == -1);
	assert (fast_floor_d (-1.) == -1);
	assert (fast_floor_d (-1.1) == -2);
	assert (fast_floor_d (-1.9) == -2);
	assert (fast_floor_d (-3.) == -3);
	assert (fast_floor_d (-3.5) == -4);
	assert (fast_floor_d (-4.) == -4);
//	assert (fast_floor_d (1000000000) == 1000000000);
}

# endif // _DEBUG
